//
// Created by LiuYou on 2021/10/22.
//

#include <unistd.h>
#include <cstdio>
#include <cstdlib>
#include <cerrno>
#include <csignal>
#include <fcntl.h>


// TODO 注意: 重要！！！！
//  使用 alarm()函数往往需要一个 标志性的变量！！！！
static volatile int isLoop = 1;


static const int CHARACTER_PER_SEC{10};
// 其实就限制了一次的读写操作是控制在10个字符的。
static const int BUF_SIZE{CHARACTER_PER_SEC};


static void alarmHandler(int sig) {
    // TODO 注意: 重要: 本次执行 alarm()会触发下一秒的 alarm()。
    //  当执行一次 SIGALRM信号处理函数的时候, 它就会为自己发出下一秒的时钟信号。
    //  即让 for(;;)循环, 一秒钟执行一次。
    alarm( 1 );
    isLoop = 0;
}


/**
 * @brief 通过MyCat来实现SlowCat
 *
 * 其实这个程序是慧琴老师带领我写的一个流控算法的实例。其实算是一个漏桶。
 *
 * 漏桶: 在本程序的read()函数那里
 * 来一万个字节或者是来十万个字节, 海量的数据来了, 我也不能着急, 不急不慢,
 * 一秒钟10个字节, 1秒钟10个字节。
 * 如果没有数据, 我就在这儿等(闲着)。
 *
 *
 * 令牌桶:
 * 闲着我就攒权限, 我有1秒钟传输10个字节的权限, 那我就攒着我不浪费。
 * 比方, 我3秒钟闲着, 那我就攒下了30个字节的权限。此时突然来了一大堆数据, 那我
 * 就可以瞬间将这一大波数据中的30个字节解决掉, 然后我攒的用完了, 我就只能一秒钟
 * 10个字节这样继续下去。这样叫做令牌桶。
 *
 * @return 0
 */
int main(int argc, char** argv) {
    if (argc < 2) {
        fprintf( stderr, "Usage: ...\n" );
        exit( 1 );
    }

    // 信号处理函数一定要在发出信号的函数之前！
    signal( SIGALRM, alarmHandler );
    alarm( 1 );

    int srcFd{};
    do {
        srcFd = open( argv[1], O_RDONLY );
        if (srcFd < 0) {
            if (errno != EINTR) {
                perror( "open()" );
                exit( 1 );
            }
        }
    } while (srcFd < 0);

    int desFd{1};

    char buf[BUF_SIZE]{};
    int pos{};
    for (;;) {

        // TODO 注意: 重要: 这里会让 CPU使用率 飙升到 100% 。
        //  如果是双核机器会看到占满一个核。
        //  原因是有盲等, 就是这个 while这个循环！！！！
        while (isLoop) {
            // TODO 注意: 重要: 这里加上 pause() 就不会出现盲等现象了,
            //  即不会看到 CPU使用率 飙升到 100%。
            pause();
        }
        isLoop = 1;


        // TODO 注意: 改进版
        //  说真的这个BUG, 我是真的发现不了, 而且即使发现了我也不会改！
        ssize_t readBytes{};
        // TODO 注意: 这里就体现了 漏桶！
        while ((readBytes = read( srcFd, buf, sizeof( buf ))) < 0) {
            if (errno == EINTR) {
                continue;
            }
            perror( "read()" );
            break;
        }
        if (readBytes == 0) {
            break;
        }


        // TODO 注意: 问题在这里, 逻辑错误了, 我没有写 pos = 0; 这条语句！
        // :(
        pos = 0;
        // 坚持写够readBytes个字节
        while (readBytes > 0) {
            ssize_t writeBytes = write( desFd, buf + pos, readBytes );
            if (writeBytes < 0) {
                if (errno == EINTR) {
                    continue;
                }
                perror( "write()" );
                close( srcFd );
                exit( 1 );
            }
            // 这里很精妙
            readBytes -= writeBytes;
            pos += (int) writeBytes;
        }

        // TODO 注意: 这里加上一条 sleep()函数调用就可以完成我们想要实现的效果, 我们要的效果
        //  但是, 不要在程序中使用 sleep()调用。所以要另寻他法！
//        sleep(1);

    }


    close( srcFd );

    return 0;
}

